import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Parse {
    //产生式
    static List<Formula> formulas;
    //终结符
    static List<String> terminals;
    //非终结符
    static List<String> nonTerminals;
    //首字符集
    static Map<String, List<String>> firsts;
    //后随字符集
    static Map<String, List<String>> follows;
    //预测表
    static Map<String, Formula> predictions;

    //入口函数
    static public void Do() {
        formulas = new ArrayList<>();
        terminals = new ArrayList<>();
        nonTerminals = new ArrayList<>();
        firsts = new HashMap<>();
        follows = new HashMap<>();
        predictions = new HashMap<>();
        setFormulas();
        setNonTerminals();
        setTerminals();
        setFirsts();
        setFollows();
        setPredictions();
    }

    //生成文法规则
    public static void setFormulas() {
        try {
            //输入原始文法文件
            File file = new File(Config.grammarPath);
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            String line, left, right;
            Formula formula;
            System.out.println("开始读取并处理文法规则:---------------------------");
            while ((line = randomAccessFile.readLine()) != null) {
                //切分出左右部分
                left = line.split("->")[0].trim();
                right = line.split("->")[1].trim();
                //右侧根据空格分开并储存
                formula = new Formula(left, right.split(" "));
                formulas.add(formula);
                System.out.println("left: " + left + " " + "right: " + right);
            }
            randomAccessFile.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    //解析文法中的非终结符
    //非终结符就是文法中所有的left
    static void setNonTerminals() {
        //加入文法中所有的left
        for (Formula formula : formulas) {
            if(nonTerminals.contains((formula.getLeft()))) {
                continue;
            } else  {
                nonTerminals.add(formula.getLeft());
            }
        }
    }

    //解析文法中的终结符
    //终结符就是文法中所有的符号去掉非终结符和空符
    static void setTerminals() {
        for (Formula formula : formulas) {
            for (String s : formula.getRight()) {
                if(nonTerminals.contains(s) || s.equals("$")) {
                    continue;
                } else {
                    terminals.add(s);
                }
            }
        }
    }

    //first集合
    static void setFirsts() {
        GenerateFirstSet first = new GenerateFirstSet(formulas, terminals, nonTerminals, firsts);
        first.setFirst();
    }

    //follow集合
    static void setFollows() {
        GenerateFollowSet follow = new GenerateFollowSet(formulas, terminals, nonTerminals, firsts, follows);
        follow.setFollow();
    }

    //预测表
    static void setPredictions() {
        GeneratePredictMap predict = new GeneratePredictMap(formulas, terminals, nonTerminals, firsts, follows, predictions);
        predict.setPredictions();
    }
    //将各种结果写入文件
    static void writeMiddleParseResultIntoFile() {
        //文法规则
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.formulaPath));
            for(Formula formula : formulas) {
                bWriter.write("left: " + formula.getLeft() + "\t" + "right:" + Arrays.toString(formula.getRight()) + "\n");
            }
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存文法规则时出错:" + e.getMessage());
        }
        System.out.println("保存文法规则成功");
        //非终结符
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.nonTerminalPath));
            nonTerminals.forEach(item -> {
                try {
                    bWriter.write(item + "\n");
                } catch (IOException e) {
                    System.out.println("保存非终结符时出错:" + e.getMessage());
                }
            });
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存非终结符时出错:" + e.getMessage());
        }
        System.out.println("保存非终结符成功");
        //终结符
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.terminalPath));
            terminals.forEach(item -> {
                try {
                    bWriter.write(item + "\n");
                } catch (IOException e) {
                    System.out.println("保存终结符时出错:" + e.getMessage());
                }
            });
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存终结符时出错:" + e.getMessage());
        }
        System.out.println("保存终结符成功");
        //first集合
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.firstSetPath));
            for (String s : firsts.keySet()) {
                bWriter.write(s + " first:" + "\t" + firsts.get(s) + "\n");
            }
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存first集合时出错:" + e.getMessage());
        }
        System.out.println("保存first集合成功");
        //follow集合
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.followSetPath));
            for (String s : follows.keySet()) {
                bWriter.write(s + " follow:" + "\t" + follows.get(s) + "\n");
            }
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存follow集合时出错:" + e.getMessage());
        }
        System.out.println("保存follow集合成功");
        //预测表
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.predictMapPath));
            for (String s : predictions.keySet()) {
                bWriter.write(s + "\t" + "文法: " + predictions.get(s).getLeft() + "->" +
                        Arrays.toString(predictions.get(s).getRight()) + "\n");
            }
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存预测表时出错:" + e.getMessage());
        }
        System.out.println("保存预测表成功");
    }
}

